package maxcache

import (
	"context"
	"github.com/pkg/errors"
	"sync"
	"time"
)

/**
 * @Author:guoqiang
 * @Date:2022/11/01
 * @Description: mapcache 使用map实现的缓存
 */

var (
	errKeyNotFound      = errors.New("cache: key 不存在")
	errKeyExpired       = errors.New("cache: key 已经过期")
	errOverCapacity     = errors.New("cache: 超过缓存最大容量")
	errFailedToSetCache = errors.New("cache: 设置键值对失败")
)

type MapCache struct {
	data          map[string]*item
	lock          sync.RWMutex
	onEvicted     func(string, []byte)
	cycleInterval time.Duration
	close         chan struct{}
}

// MapCacheOption option
type MapCacheOption func(*MapCache)

// MapWithCycleInterval 设置清理周期
func MapWithCycleInterval(interval time.Duration) MapCacheOption {
	return func(m *MapCache) {
		m.cycleInterval = interval
	}
}

// NewMapCache 创建一个mapcache
func NewMapCache(opts ...MapCacheOption) *MapCache {
	res := &MapCache{
		data:          make(map[string]*item),
		cycleInterval: time.Second * 10,
	}

	for _, opt := range opts {
		opt(res)
	}

	return res
}

// Get 获取缓存
func (m *MapCache) Get(ctx context.Context, key string) ([]byte, error) {
	m.lock.RLock()
	val, ok := m.data[key]
	m.lock.RUnlock()

	if !ok {
		return nil, errKeyNotFound
	}

	if val.expired() {
		m.lock.Lock()
		defer m.lock.Unlock()

		val, ok = m.data[key]

		if !ok {
			return nil, errKeyNotFound
		}

		if val.expired() {
			m.delete(ctx, key)
			return nil, errKeyExpired
		}
	}

	return val.val, nil
}

func (m *MapCache) delete(ctx context.Context, key string) {
	val, ok := m.data[key]
	if !ok {
		return
	}

	delete(m.data, key)

	if m.onEvicted != nil {
		m.onEvicted(key, val.val)
	}
}

// Delete 删除缓存
func (m *MapCache) Delete(ctx context.Context, key string) error {
	m.lock.Lock()
	defer m.lock.Unlock()

	m.delete(ctx, key)

	return nil
}

// Set 设置缓存
func (m *MapCache) Set(ctx context.Context, key string, val []byte, expiration time.Duration) error {
	m.lock.Lock()
	defer m.lock.Unlock()

	m.data[key] = &item{
		val:        val,
		expiration: time.Now().Add(expiration),
	}

	return nil
}

// Close 关闭
func (m *MapCache) Close() error {
	m.lock.Lock()
	defer m.lock.Unlock()

	m.close <- struct{}{}

	if m.onEvicted != nil {
		for key, val := range m.data {
			m.onEvicted(key, val.val)
		}
	}

	m.data = nil
	return nil
}

// LoadAndDelete 加载并删除
func (m *MapCache) LoadAndDelete(ctx context.Context, key string) ([]byte, error) {
	m.lock.Lock()
	defer m.lock.Unlock()

	val, ok := m.data[key]
	if !ok {
		return nil, errKeyNotFound
	}

	m.delete(ctx, key)
	return val.val, nil
}

// OnEvicted 设置删除回调
func (m *MapCache) OnEvicted(f func(string, []byte)) {
	m.onEvicted = f
}

// checkCircle 定时检查是否过期
func (m *MapCache) checkCircle() {
	go func() {
		t := time.NewTicker(m.cycleInterval)

		for {
			select {
			case <-t.C:
				for i := 0; i < 5; i++ {
					m.lock.Lock()
					for key, val := range m.data {
						if val.expired() {
							m.delete(context.Background(), key)
						}

						break
					}
					m.lock.Unlock()
				}

			case <-m.close:
				close(m.close)
				return
			}
		}
	}()
}
